package cn.demomaster.quickdb_compiler;


import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

import cn.demomaster.quickdb_annotations.Column;
import cn.demomaster.quickdb_annotations.Table;

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({"cn.demomaster.quickdb_annotations.Table"})
public class MapperProcessor extends AbstractProcessor {
    public static String BindClassSuffix = "$$BindClass";
    private Filer mFilerUtils;
    private Types mTypesUtils;
    private Elements mElementsUtils;
    Messager messager;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        println("QuickDB MapperProcessor init");
        mFilerUtils = processingEnv.getFiler();
        mTypesUtils = processingEnv.getTypeUtils();
        mElementsUtils = processingEnv.getElementUtils();

        messager = processingEnv.getMessager();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        //println("MapperProcessor getSupportedAnnotationTypes");
        return super.getSupportedAnnotationTypes();
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        //println("MapperProcessor getSupportedSourceVersion");
        return super.getSupportedSourceVersion();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {
        println("start process ");
        // 获取所有被 @Table 注解标记的元素
        for (Element element : roundEnv.getElementsAnnotatedWith(Table.class)) {
            if (element instanceof TypeElement typeElement) { // 确保是类型元素（类、接口或枚举）
                // 解析 @Table 注解的信息
                Table dbTableAnnotation = typeElement.getAnnotation(Table.class);
                String tableName = dbTableAnnotation.name().isEmpty()
                        ? typeElement.getSimpleName().toString()
                        : dbTableAnnotation.name();
                String databaseName = dbTableAnnotation.databaseName();

                // 打印调试信息
                messager.printMessage(Diagnostic.Kind.NOTE, "Found @Table annotation on: " + typeElement.getQualifiedName());
                messager.printMessage(Diagnostic.Kind.NOTE, "Table name: " + tableName);

                // 这里你可以添加更多逻辑，例如生成代码或其他处理
                // 例如，你可以根据 tableName 和 typeElement 生成对应的 DAO 或其他辅助类
                //                String code = generateCode(typeElement);
                String helperClassName = typeElement.getQualifiedName() + BindClassSuffix;

                println("MapperProcessor helperClassName= " + helperClassName);
                String code = generateCode(typeElement);
                try {
                    JavaFileObject jfo = mFilerUtils.createSourceFile(helperClassName, typeElement);
                    Writer writer = jfo.openWriter();
                    println("code = " + code);
                    writer.write(code);
                    writer.flush();
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    public void println(String msg) {
        //printStr = printStr + msg + "\n";
        Messager messager = processingEnv.getMessager();
        messager.printMessage(Diagnostic.Kind.NOTE, msg);
    }


    /**
     * 生成代碼
     *
     * @param typeElement
     * @return
     */
    private String generateCode(TypeElement typeElement) {
        Table dbTableAnnotation = typeElement.getAnnotation(Table.class);
        String tableName = dbTableAnnotation.name().isEmpty()
                ? typeElement.getSimpleName().toString()
                : dbTableAnnotation.name();
        String databaseName = dbTableAnnotation.databaseName();
        String simpleName = typeElement.getSimpleName().toString();
        String mapperClassName = simpleName + BindClassSuffix;
        String packageName = mElementsUtils.getPackageOf(typeElement).getQualifiedName().toString();//MapperProcessor.class.getPackageName();//

        List<ColumnElement> columnElementList = getColumnElementList(typeElement);

        // 使用 StringBuilder 构建代码
        StringBuilder codeBuilder = new StringBuilder();
        codeBuilder.append("package " + packageName + ";\n" +
                "\n" +
                "\n" +
                "import " + typeElement.getQualifiedName() + ";\n" +
                "import cn.demomaster.quickdb.mapper.CursorMapper;\n" +
                "import cn.demomaster.quickdb.model.ColumnInfo;\n" +
                "import cn.demomaster.quickdb.utils.SqlValueFormatter;\n" +
                "import android.database.Cursor;\n" +
                "import java.util.ArrayList;\n" +
                "import java.util.List;" +
                "\n" +
                "import " + typeElement.getQualifiedName() + ";" +
                "\n" +
                "public class " + mapperClassName + " extends CursorMapper<" + simpleName + "> {\n");
        codeBuilder.append(
                "List<ColumnInfo> columnInfoList;\n" +
                        "    " + mapperClassName + "(){\n" +
                        "        columnInfoList = new ArrayList<>();\n");

        for (ColumnElement columnElement : columnElementList) {
            codeBuilder.append("        columnInfoList.add(new ColumnInfo(\"" + columnElement.getColumnName() + "\",\""
                    + columnElement.getColumnType() + "\",\""
                    + columnElement.getFieldName() + "\",\""
                    + columnElement.getFieldType() + "\","
                    + columnElement.isPrimaryKey() + ","
                    + columnElement.isAutoIncrement() + ","
                    + (columnElement.getDefaultValue()==null?"null":("\""+columnElement.getDefaultValue()+"\"")) + ","
                    + (columnElement.getComment()==null?"null":("\""+columnElement.getComment()+"\"")) + ","
                    + columnElement.isNotNull() + ","
                    + columnElement.isUnique() + ","
                    + (columnElement.getValueFormat()==null?"null":("\""+columnElement.getValueFormat()+"\"")) +"));\n");

        }
        codeBuilder.append(" tableName = " + ((tableName == null | tableName.isEmpty()) ? simpleName : ("\"" + tableName + "\"")) + ";\n");

        codeBuilder.append(

                "    }\n" +
                        "    \n" +
                        "    public List<ColumnInfo> getColumnInfoList() {\n" +
                        "        return columnInfoList;\n" +
                        "    } \n"
        );

        codeBuilder.append("    @Override\n" +
                "    public " + simpleName + " createEntity(Cursor cursor) {\n" +
                "        if (cursor != null) {\n" +
                "            try {\n" +
                "                " + simpleName + " entity = new " + simpleName + "();\n");

        // 动态生成字段映射
        //List<String> fieldMappings = getFieldsMapping(columnElementList);
        for (ColumnElement element : columnElementList) {
            codeBuilder.append("int "+element.getFieldName()+"_index = cursor.getColumnIndex(\""+element.getColumnName()+"\");\n");
            String columnTypeStr = null;
            if ("int".equals(element.getFieldType()) || "java.lang.Integer".equals(element.getFieldType())) {
                columnTypeStr = "Int";
            } else if ("float".equals(element.getFieldType()) || "java.lang.Float".equals(element.getFieldType())) {
                columnTypeStr = "Float";
            } else if ("java.lang.String".equals(element.getFieldType())) {
                columnTypeStr = "String";
            }else if ("long".equals(element.getFieldType()) ||"java.lang.Long".equals(element.getFieldType())) {
                columnTypeStr = "Long";
            }else if ("boolean".equals(element.getFieldType()) ||"java.lang.Boolean".equals(element.getFieldType())) {
                columnTypeStr = "Int";
                //columnTypeStr = element.getColumnType();
            }
            if(columnTypeStr!=null) {
                codeBuilder.append("if(" + element.getFieldName() + "_index!=-1){");
                if ("boolean".equals(element.getFieldType()) || "java.lang.Boolean".equals(element.getFieldType())) {
                    codeBuilder.append(String.format("entity.set%s(cursor.get%s(%s)==1);\n}",
                            GetterSetterGenerator.capitalize(element.getFieldName(), element.getFieldType()), columnTypeStr, element.getFieldName() + "_index"));
                } else {
                    codeBuilder.append(String.format("entity.set%s(cursor.get%s(%s));\n}",
                            GetterSetterGenerator.capitalize(element.getFieldName(), element.getFieldType()), columnTypeStr, element.getFieldName() + "_index"));
                }
            }

//            codeBuilder.append("int index = cursor.getColumnIndex(\"\");\n" +
//                    "                if(index!=-1) {\n" +
//                    "                    entity.setId(cursor.getInt(index));\n" +
//                    "                }");
            //codeBuilder.append("                ").append(fieldMapping).append(";\n");
        }
        codeBuilder.append(
                "                return entity;\n" +
                        "            } catch (Exception e) {\n" +
                        "                e.printStackTrace();\n" +
                        "            }\n" +
                        "        }\n" +
                        "        return null;\n" +
                        "    }\n" +
                        "@Override\n" +
                        "    public String getTableName() {\n" +
                        "        return tableName;" +
                        "    }\n" +
                        "\n" +
                        "    @Override\n" +
                        "    public String getDataBaseName() {\n" +
                        "        return " + ((databaseName == null | databaseName.isEmpty()) ? "null" : ("\"" + databaseName + "\"")) + ";\n" +
                        "    }" +
                        "@Override\n" +
                        "    public String generateInsertSql(" + simpleName + " obj) {\n" +
                        "StringBuilder columnsStringBuilder = new StringBuilder();\n" +
                        "        columnsStringBuilder.append(\"");
        String columnNames = "";
        boolean isFirst = true;
        for (ColumnElement columnElement : columnElementList) {
            if (!columnElement.isAutoIncrement()) {
                columnNames += ("" + (isFirst ? "" : ",") + columnElement.getColumnName());
                codeBuilder.append("" + (isFirst ? "" : ",") + columnElement.getColumnName());
                if (isFirst) {
                    isFirst = false;
                }
            }
        }
        codeBuilder.append(" \");\n" +
                "\n" +
                "        StringBuilder valuesStringBuilder = new StringBuilder();\n");
        // 动态生成字段映射
        boolean isFirstValue = true;
        for (ColumnElement columnElement : columnElementList) {
            codeBuilder.append("valuesStringBuilder.append(\"" + (isFirstValue ? "" : ",") + "\"+SqlValueFormatter.toSqlValue(obj." + GetterSetterGenerator.generateGettersName(columnElement.getFieldName(), columnElement.getFieldType()) + "()));\n");
            if (isFirstValue) {
                isFirstValue = false;
            }
        }

        codeBuilder.append("String sql = String.format(\"INSERT INTO " + tableName + "(%s) VALUES(%s)\",columnsStringBuilder.toString(),valuesStringBuilder.toString());" +
                "       return sql;\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public String generateCreateTableSql() {\n" +
                "String sql=\"CREATE TABLE if not exists " + tableName + "(");
        boolean isFirstCreateTableSql = true;
        for (ColumnElement columnElement : columnElementList) {
            codeBuilder.append((isFirstCreateTableSql ? "" : ",") + columnElement.getColumnName() + " " + columnElement.getColumnType() + getConstraints(columnElement));
            if (isFirstCreateTableSql) {
                isFirstCreateTableSql = false;
            }
        }
        codeBuilder.append(")");
        codeBuilder.append(
                "\";  " +
                        "   return sql;\n" +
                        "    }\n");
        codeBuilder.append("@Override\n" +
                "    public String generateInsertSql(List<" + simpleName + "> models, Class<" + simpleName + "> clazz) {\n");
        codeBuilder.append("String columnNames = \"" + columnNames + "\";\n" +
                "        StringBuilder columnsStringBuilder = new StringBuilder();\n" +
                "        columnsStringBuilder.append(\"INSERT INTO " + tableName + " (\"+columnNames+\") VALUES \");\n");

        codeBuilder.append("boolean isFirst = true;\n" +
                "        for(" + simpleName + " model:models){\n" +
                "String values;\n" +
                "            if(isFirst){\n" +
                "                isFirst = false;\n" +
                "            }\n " +
                "values =");
        boolean isFirstListValue = true;
        for (ColumnElement columnElement : columnElementList) {
            if (!columnElement.isAutoIncrement()) {
                codeBuilder.append((isFirstListValue ? "\"\"+" : "+\",\"+") + " SqlValueFormatter.toSqlValue(model." + GetterSetterGenerator.generateGettersName(columnElement.getFieldName(), columnElement.getFieldType()) + "())");
                if (isFirstListValue) {
                    isFirstListValue = false;
                }
            }
        }
        codeBuilder.append(
                ";\n" +
                        "columnsStringBuilder.append(\"(\"+values+\"),\\n\");" +
                        "}\n");

        codeBuilder.append(
                "      String sql= columnsStringBuilder.toString().substring(0,columnsStringBuilder.toString().lastIndexOf(\",\"));\n" +
                        "    sql+=\"; \";\n" +
                        " return sql;   }\n");
        codeBuilder.append("@Override\n" +
                "    public String generateModifySql(List<" + simpleName + "> models, Class<" + simpleName + "> clazz) {\n" +
                "        StringBuilder queryBuilder = new StringBuilder(\"UPDATE \"+tableName+\" set \");\n" +
                "        for (" + simpleName + " model : models){\n");

        boolean isFirstModifyValue = true;
        ColumnElement primaryKeyColumnElement = null;
        for (ColumnElement columnElement : columnElementList) {
            if (columnElement.isPrimaryKey()) {
                primaryKeyColumnElement = columnElement;
            }
            if (columnElement.isAutoIncrement()) {
                continue;
            }
            if (isFirstModifyValue) {
                isFirstModifyValue = false;
            } else {
                codeBuilder.append("queryBuilder.append(\",\");\n");
            }
            codeBuilder.append(
                    "queryBuilder.append(\"" + columnElement.getColumnName() + "=\"+SqlValueFormatter.toSqlValue(model." + GetterSetterGenerator.generateGettersName(columnElement.getFieldName(), columnElement.getFieldType()) + "()));\n");
        }
        if (primaryKeyColumnElement != null) {
            codeBuilder.append("queryBuilder.append(\" where " + primaryKeyColumnElement.getColumnName() + "=\"+model." + GetterSetterGenerator.generateGettersName(primaryKeyColumnElement.getFieldName(), primaryKeyColumnElement.getFieldType()) + "()+\";\");\n");
        }
        codeBuilder.append(
                "}\n" +
                        "        return queryBuilder.toString();\n" +
                        "    }\n");

        codeBuilder.append("@Override\n" +
                "    public String getFieldValue("+simpleName+" obj, String fieldName) {\n");

        boolean isFirstGetValueFromModel = true;
        for (ColumnElement columnElement : columnElementList) {
            codeBuilder.append("if(fieldName.equals(\""+columnElement.getFieldName()+"\")){\n");
            codeBuilder.append("return String.valueOf(obj."+GetterSetterGenerator.generateGettersName(columnElement.getFieldName(), columnElement.getFieldType())+"());\n" +
                    "}");
            if(isFirstGetValueFromModel){
                isFirstGetValueFromModel = false;
            }
        }
        codeBuilder.append(
                "        return null;\n" +
                "    }");

        codeBuilder.append(
                "}\n");
        return codeBuilder.toString();
    }

    private List<ColumnElement> getColumnElementList(TypeElement typeElement) {
        List<ColumnElement> columnElementList = new ArrayList<>();
        for (Element element : typeElement.getEnclosedElements()) {
            if (element instanceof VariableElement variableElement) {
                // 获取字段名和类型
                String fieldName = variableElement.getSimpleName().toString();
                String fieldType = variableElement.asType().toString();

                // 尝试获取 @Column 注解
                Column sqlObj = variableElement.getAnnotation(Column.class);
                String columnName;
                String columnType;
                boolean isPrimaryKey = false;
                boolean isAutoIncrement = false;
                String defaultValue = null;
                String comment = null;
                String valueFormat = null;
                boolean isNullable = false;
                boolean isUnique = false;
                if (sqlObj != null) {
                    columnName = sqlObj.name().isEmpty() ? fieldName : sqlObj.name();
                    columnType = sqlObj.typeAffinity().toString().equalsIgnoreCase("NONE") ? getSqliteType(fieldType) : sqlObj.typeAffinity();
                    isPrimaryKey = sqlObj.primaryKey();
                    isAutoIncrement = sqlObj.autoIncrement();
                    defaultValue = sqlObj.defaultValue();
                    comment = sqlObj.comment();
                    valueFormat = sqlObj.valueFormat();
                    isNullable = sqlObj.notNull();
                    isUnique = sqlObj.unique();

                } else {
                    // 如果没有 @SQLObj 注解，默认使用字段名作为列名
                    columnName = fieldName;
                    columnType = getSqliteType(fieldType);
                }
                columnElementList.add(new ColumnElement(columnName, columnType, fieldName, fieldType, isPrimaryKey, isAutoIncrement, defaultValue, comment, isNullable, isUnique, valueFormat));

            }
        }
        return columnElementList;
    }

    public static String getSqliteType(String fieldType) {
        // 根据字段类型生成不同的 set 方法调用
        if ("int".equals(fieldType) || "java.lang.Integer".equals(fieldType)
                || "long".equals(fieldType) || "java.lang.Long".equals(fieldType)) {
            return "INTEGER";
        } else if ("float".equals(fieldType) || "java.lang.Float".equals(fieldType)
                || "double".equals(fieldType) || "java.lang.Double".equals(fieldType)) {
            return "REAL";
        } else if ("java.lang.String".equals(fieldType)) {
            return "TEXT";
        }
        return "NONE";
    }

    // 辅助方法：根据 TypeElement 获取字段映射代码
    private List<String> getFieldsMapping(List<ColumnElement> columnElementList) {
        List<String> mappings = new ArrayList<>();
        for (ColumnElement element : columnElementList) {
            // 根据字段类型生成不同的 set 方法调用
            if (("int".equals(element.getFieldType()) || "java.lang.Integer".equals(element.getFieldType()))
            ||("long".equals(element.getFieldType()) || "java.lang.Long".equals(element.getFieldType()))){
                mappings.add(String.format("entity.set%s(cursor.getInt(cursor.getColumnIndexOrThrow(\"%s\")))",
                        GetterSetterGenerator.capitalize(element.getFieldName(),element.getFieldType()), element.getColumnName()));
            } else if ("float".equals(element.getFieldType()) || "java.lang.Float".equals(element.getFieldType())) {
                mappings.add(String.format("entity.set%s(cursor.getFloat(cursor.getColumnIndexOrThrow(\"%s\")))",
                        GetterSetterGenerator.capitalize(element.getFieldName(),element.getFieldType()), element.getColumnName()));
            } else if ("java.lang.String".equals(element.getFieldType())) {
                mappings.add(String.format("entity.set%s(cursor.getString(cursor.getColumnIndexOrThrow(\"%s\")))",
                        GetterSetterGenerator.capitalize(element.getFieldName(),element.getFieldType()), element.getColumnName()));
            } else {
                // 处理其他类型（如果需要）
                messager.printMessage(Diagnostic.Kind.WARNING, "Unsupported field type: " + element.getFieldType());
            }
        }
        return mappings;
    }

    public static String getConstraints(ColumnElement con) {
        String constraints = "";
        if (con.isPrimaryKey()) {
            constraints += " PRIMARY KEY";
            if (con.isAutoIncrement()) {
                constraints += " AUTOINCREMENT";
            }
            constraints += " NOT NULL";
            constraints += " UNIQUE";
        } else {
            if (con.isNotNull()) {
                constraints += " NOT NULL";
            }
            if (con.isUnique()) {
                constraints += " UNIQUE";
            }
        }
        return constraints;
    }

}


